/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

/**
 *
 * @author Danang
 */
public class FileListing {
    public static String newLine = System.getProperty("line.separator");
    public static String tabChar = "\t";
    public FileListing() {
    }

    /**
     * Recursively walk a directory tree and return a List of all Files found;
     * the List is sorted using File.compareTo().
     *
     * @param aStartingDir is a valid directory, which can be read.
     */
    static public List<File> getFileListing(File aStartingDir) throws FileNotFoundException {
        System.out.println("Get file list from directory " + aStartingDir.getPath());
        validateDirectory(aStartingDir);
        List<File> result = getFileListingNoSort(aStartingDir);
        System.out.println("Number of files : " + result.size());
        return result;
    }

    // PRIVATE //
    static private List<File> getFileListingNoSort(File aStartingDir) throws FileNotFoundException {
        List<File> result = new ArrayList<File>();
        File[] filesAndDirs = aStartingDir.listFiles();
        List<File> filesDirs = Arrays.asList(filesAndDirs);
        for (File file : filesDirs) {
            if (!file.getName().contains("all_documents")) {
                if (!file.isFile()) {
                    //must be a directory
                    //recursive call!
                    List<File> deeperList = getFileListingNoSort(file);
                    result.addAll(deeperList);
                } else {
                    result.add(file); //always add, even if directory
                }
            }
        }
        return result;
    }

    // get folder saja secara recursive
    static public List<File> getFolderListing(File aStartingDir) throws FileNotFoundException {
        System.out.println("Get folder list from directory " + aStartingDir.getPath());
        validateDirectory(aStartingDir);
        List<File> result = getFolderListingNoSort(aStartingDir);
        System.out.println("Number of folder : " + result.size());
        return result;
    }

    // PRIVATE //
    static private List<File> getFolderListingNoSort(File aStartingDir) throws FileNotFoundException {
        List<File> result = new ArrayList<File>();
        File[] filesAndDirs = aStartingDir.listFiles();
        List<File> filesDirs = Arrays.asList(filesAndDirs);
        for (File file : filesDirs) {
            if (!file.isFile()) {
                result.add(file);
            } else {
                //result.add(file); //always add, even if directory
            }
        }
        return result;
    }

    // get file saja
    static public List<File> getJustFileListing(File aStartingDir) throws FileNotFoundException {
        //System.out.println("Get just file list from directory " + aStartingDir.getPath());
        validateDirectory(aStartingDir);
        List<File> result = getJustFileListingNoSort(aStartingDir);
        //System.out.println("Number of files : " + result.size());
        return result;
    }

    // PRIVATE //
    static private List<File> getJustFileListingNoSort(File aStartingDir) throws FileNotFoundException {
        List<File> result = new ArrayList<File>();
        File[] filesAndDirs = aStartingDir.listFiles();
        List<File> filesDirs = Arrays.asList(filesAndDirs);
        for (File file : filesDirs) {
            if (file.isFile()) {
                result.add(file);
            } else {
                //result.add(file); //always add, even if directory
            }
        }
        return result;
    }

    // sorting list file secara ascending
    public static void sortFilesAscending(List<File> files) {
        Collections.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                int result = o1.getName().compareTo(o2.getName());
                return result == 0 ? 0 : result > 0 ? 1 : -1;
            }
        });
    }

    // sorting list file secara descending
    public static void sortFilesDescending(List<File> files) {
        Collections.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                int result = o1.getName().compareTo(o2.getName());
                return result == 0 ? 0 : result > 0 ? -1 : 1;
            }
        });
    }

    /**
     * Directory is valid if it exists, does not represent a file, and can be
     * read.
     */
    static private void validateDirectory(File aDirectory) throws FileNotFoundException {
        if (aDirectory == null) {
            throw new IllegalArgumentException("Directory should not be null.");
        }
        if (!aDirectory.exists()) {
            throw new FileNotFoundException("Directory does not exist: " + aDirectory);
        }
        if (!aDirectory.isDirectory()) {
            throw new IllegalArgumentException("Is not a directory: " + aDirectory);
        }
        if (!aDirectory.canRead()) {
            throw new IllegalArgumentException("Directory cannot be read: " + aDirectory);
        }
    }

    // delete directory secara recursive
    public static void delete(File file)
            throws IOException {

        if (file.isDirectory()) {

            //directory is empty, then delete it
            if (file.list().length == 0) {

                file.delete();
                /*System.out.println("Directory is deleted : "
                        + file.getAbsolutePath());*/

            } else {

                //list all the directory contents
                String files[] = file.list();

                for (String temp : files) {
                    //construct the file structure
                    File fileDelete = new File(file, temp);

                    //recursive delete
                    delete(fileDelete);
                }

                //check the directory again, if empty then delete it
                if (file.list().length == 0) {
                    file.delete();
                    /*System.out.println("Directory is deleted : "
                            + file.getAbsolutePath());*/
                }
            }

        } else {
            //if file, then delete it
            file.delete();
            /*System.out.println("File is deleted : " + file.getAbsolutePath());*/
        }
    }
    
    
    // write list file ke file_list
    public static void writeFileListingToFile(String mailDirName, List<File> files, String outDir) throws Exception {
        String pathFolder = outDir+"email_index/";
        if (!DirectoryCreator.createOrCheckIfExistDir(pathFolder)) {
            System.out.println("can't make directory at "+pathFolder);
            throw new Exception("Error : can't make dir at "+pathFolder);
        }
        
        RandomAccessFile raf = new RandomAccessFile(new File(pathFolder+"file_list"), "rw");
        int count = 0;
        for (File a : files) {
            raf.writeBytes(count+" ");
            File tes = a;
            while(!mailDirName.equals(tes.getName())) {
                raf.writeBytes("\""+tes.getName()+"\" ");
                tes = tes.getParentFile();
            }
            raf.writeBytes("\n");
            
            count++;
        }
        raf.close();
    }
    
    RandomAccessFile writer,writer2;
    String mailDir;
    long totalMessageLength=0;
    // init write file_list
    public void initWriteMessageID(String rootDir, String mailDirName) throws Exception {
        String pathFolder = rootDir+"email_index/";
        if (!DirectoryCreator.createOrCheckIfExistDir(pathFolder)) {
            System.out.println("can't make directory at "+pathFolder);
            throw new Exception("Error : can't make dir at "+pathFolder);
        }
        
        writer = new RandomAccessFile(new File(pathFolder+"file_list"), "rw");
        writer2 = new RandomAccessFile(new File(pathFolder+"statistic_file"), "rw");
        this.mailDir = mailDirName;
    }
    
    public void writeMessageID(int messageID, File mail, int messageLength) throws Exception {
        totalMessageLength += messageLength;
        File tes = mail;
        StringBuilder sb = new StringBuilder();
        writer.writeBytes(messageID+" ");
        while(!this.mailDir.equals(tes.getName())) {
            String s = tes.getName()+"/";
            sb.insert(0,s);
            tes = tes.getParentFile();
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append(" ").append(messageLength);
        sb.append("\n");
        writer.writeBytes(sb.toString());
    }
    
    public void closeFile() throws IOException {
        writer.close();
        writer2.close();
    }
    
    public void writeStatisticFile(int jumlahFile) throws IOException {
        writer2.writeBytes("Jumlah_file "+jumlahFile+"\n");
        double avg = (double)totalMessageLength/jumlahFile;
        writer2.writeBytes("average "+avg);
    }
    
    public static void writeNestedIndexForFileList(String outDir, int numOfFiles) throws Exception {
        String pathFolder = outDir+"email_index/";
        String resourceFile = pathFolder+"file_list";
        String outFile = pathFolder+"nested_file";
        
        int numBagi = 10;
        int numOfNested = (int)(Math.log10(numOfFiles)/Math.log10(numBagi))+1;
        File outFileNested;
        File sourceFileNested = new File(resourceFile);
        for (int i=1;i<=numOfNested;i++) {
            outFileNested = new File(outFile+i);
            RandomAccessFile reader = new RandomAccessFile(sourceFileNested, "r");
            RandomAccessFile writer = new RandomAccessFile(outFileNested, "rw");
            String str;
            int count = 0;
            long pos = 0;
            while((str=reader.readLine())!=null) {
                if (count%numBagi==0) {
                    String[] s = str.split(" ");
                    writer.writeBytes(s[0]+" "+pos+"\n");
                }
                count++;
                pos = reader.getFilePointer();
            }
            reader.close();
            writer.close();
            sourceFileNested = outFileNested;
        }
    }
    
    List<File> nestedlist;
    HashMap<String,RandomAccessFile> nestedMapFile;
    File file_list,statistic_file;
    RandomAccessFile file_listReader;
    public void initReadNestedIndexFileList(String outDir) throws Exception{
        Timer t = new Timer();
        String pathFolder = outDir+"email_index/";
        nestedlist = FileListing.getJustFileListing(new File(pathFolder));
        FileListing.sortFilesDescending(nestedlist);
        file_list=null;
        statistic_file=null;    
        nestedMapFile = new HashMap<String,RandomAccessFile>();
        for (File f : nestedlist) {
            if (f.getName().equals("file_list")) {
                file_list = f;
            } else if (f.getName().equals("statistic_file")) {
                statistic_file = f;
            } else {
                RandomAccessFile aa = new RandomAccessFile(f, "r");
                nestedMapFile.put(f.getName(), aa);
            }
        }
        nestedlist.remove(file_list);
        nestedlist.remove(statistic_file);
        file_listReader = new RandomAccessFile(file_list, "r");
        readAllDocumentLength();
        t.printExecution_time("Finished init file list reader");
    }
    
    public String readNestedIndexFileList(int docID) throws Exception { 
        long pos = 0;
        for (File f : nestedlist) {
            RandomAccessFile raf = nestedMapFile.get(f.getName());
            raf.seek(pos);
            String str;
            int compare=-1;
            while((str=raf.readLine())!=null) {
                String[] split = str.split(" ");
                compare = Integer.parseInt(split[0]);
                if (compare>=docID) {
                    if (compare==docID) {
                        pos = Long.parseLong(split[1]);
                    }
                    break;
                }
                pos = Long.parseLong(split[1]);
            }
        }
        
        file_listReader.seek(pos);
        String res="";
        int compare = -1;
        while ((res = file_listReader.readLine())!=null) {
            String[] split = res.split(" ");
            compare = Integer.parseInt(split[0]);
            if (compare==docID) {
                break;
            }
        }
        
        return res;
    }
    
    HashMap<Integer,Long> allDocumentLength;
    void readAllDocumentLength() throws Exception {
        allDocumentLength = new HashMap<Integer, Long>();
        file_listReader.seek(0);
        String str;
        int count = 0;
        while((str=file_listReader.readLine())!=null) {
            String[] split = str.split(" ");
            int id = Integer.parseInt(split[0]);
            long docLength = Long.parseLong(split[2]);
            allDocumentLength.put(id, docLength);
            count++;
            if (count%100000==0) {
                System.out.println("Reading doc length "+count);
            }
        }
    }
    
    public void closeReaderNestedIndex() throws Exception {
        for (File f : nestedlist) {
            nestedMapFile.get(f.getName()).close();
        }
        file_listReader.close();
    }
    
    public long getDocumentLength(int docID) throws Exception{
        String res = readNestedIndexFileList(docID);
        String[] split = res.split(" ");
        return Long.parseLong(split[2]);
    }
    
    public long getDocumentLengthFromHashMap(int docID) {
        return allDocumentLength.get(docID);
    }
    
    public File getDocumentMail(int docID, String mailDir) throws Exception {
        String res = readNestedIndexFileList(docID);
        String[] split = res.split(" ");
        File mailDirectory = new File(mailDir);
        StringBuilder path=new StringBuilder();
        String os = System.getProperty("os.name");
        if (os.toLowerCase().contains("linux"))  {
            path.append(mailDirectory.getPath()).append("/").append(split[1]);
        } else { // windows
            path.append(mailDirectory.getPath()).append("/").append(split[1]);
            path.deleteCharAt(path.length()-1);
        }
        return new File(path.toString());
    }
    
    int jumlahFile_statistik;
    double average_length;
    public void readStatisticFile(String rootDir) throws Exception {
        String pathFolder = rootDir+"email_index/";
        File statisticFile = new File(pathFolder+"statistic_file");
        BufferedReader br = new BufferedReader(new FileReader(statisticFile));
        String str1=br.readLine(); // jumlah file
        String str2=br.readLine(); // average
        String[] sp1 = str1.split(" ");
        jumlahFile_statistik = Integer.parseInt(sp1[1]);
        String[] sp2 = str2.split(" ");
        average_length = Double.parseDouble(sp2[1]);
        
        br.close();
    }
    
    public int getJumlahMail() {
        return jumlahFile_statistik;
    }
    
    public double getAverageLengthMail() {
        return average_length;
    }
    
    public static String getSomeContentOnMail(File f, long pos) throws Exception {
        RandomAccessFile r = new RandomAccessFile(f, "r");
        r.seek(pos);
        byte[] b = new byte[50];
        r.read(b);
        return new String(b);
    }
    

    
    
//    public static void main(String[] args) throws Exception {
//        FileListing fl = new FileListing();
//        String rootDir = "/home/danang/output_index/"; // folder directory output_index
//        // tes get directory length
//        fl.initReadNestedIndexFileList(rootDir);
//        
//        System.out.println("Search 491 "+fl.getDocumentLength(491));
//        System.out.println("Search 363 "+fl.getDocumentLength(363));
//        System.out.println("Search 21 "+fl.getDocumentLength(21));
//        System.out.println("Search 1633 "+fl.getDocumentLength(1633));
//        
//        fl.closeReaderNestedIndex();
//        ///////////////////////////
//        
//        // tes get statistic file
//        fl.readStatisticFile(rootDir);
//        System.out.println("Jumlah mail "+fl.getJumlahMail());
//        System.out.println("Average mail "+fl.getAverageLengthMail());
//        //////////////////////////
//    }
    
}
